home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Hottest 6
/
Hottest 6 (1996)(PDSoft)[!].iso
/
software
/
emulators
/
vgb_amiga_0.3
/
gb.c
< prev
next >
Wrap
C/C++ Source or Header
|
1978-11-24
|
15KB
|
498 lines
/** VGB: portable GameBoy emulator ***************************/
/** **/
/** GB.c **/
/** **/
/** This file contains the portable part of the GameBoy **/
/** hardware emulation. See GB.h for #defines related to **/
/** drivers and GameBoy hardware. **/
/** **/
/** Copyright (C) Marat Fayzullin 1995 **/
/** You are not allowed to distribute this software **/
/** commercially. Please, notify me, if you make any **/
/** changes to this file. **/
/*************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "GB.h"
byte Verbose = 1; /* Verboseness level */
byte *RAM=NULL; /* Pointer to Z80 address space (64kB) */
int VPeriod = 6000; /* Number of Z80 cycles between VBlanks */
int UPeriod = 1; /* Number of VBlanks per screen update */
int TPeriod; /* Number of Z80 cycles per screen scanline */
byte LineDelay = 1; /* When 1, CMPLINE interrupts are delayed */
char *SaveName = NULL; /* .sav file name */
struct /* Cheat list to be filled before StartGB() */
{ /* is called */
byte Value,Bank;
word Address;
} Cheats[MAXCHEAT];
int CheatCount = 0; /* Number of cheats in the list */
FILE *SerialOut; /* Output stream emulating the serial line */
FILE *SerialIn; /* Input stream emulating the serial line */
byte SerialBuf; /* Next byte to output via the serial line */
byte MBCType; /* MBC type: 1 for MBC2, 0 for MBC1 */
byte *ROMMap[256]; /* Addresses of ROM banks */
byte ROMBank; /* Number of ROM bank currently used */
byte ROMMask; /* Mask for the ROM bank number */
int ROMBanks; /* Total number of ROM banks */
byte *RAMMap[256]; /* Addresses of RAM banks */
byte RAMBank; /* Number of RAM bank currently used */
byte RAMMask; /* Mask for the RAM bank number */
int RAMBanks; /* Total number of RAM banks */
byte JoyState = 0xFF; /* Joystick state: START.SELECT.B.A.D.U.L.R */
byte BPal[4]; /* Background palette */
byte SPal0[4],SPal1[4]; /* Sprite palettes */
byte *ChrGen; /* Character generator */
byte *BgdTab,*WndTab; /* Background and window character tables */
byte SprFlag = 0; /* <>0: sprites were enabled during the frame */
void DoWrite(word A,byte V);
void M_WRMEM(word A,byte V)
{
switch(A&0xE000)
{
case 0x0000:
case 0x6000:
if(Verbose&0x02) printf("Wrote %Xh to ROM at %Xh\n",V,A);
return;
case 0x2000:
if(MBCType&&((A&0xFF00)!=0x2100)) return;
V&=ROMMask;
if(ROMBank!=V)
{
ROMBank=V;
if(ROMMap[V]) memcpy(RAM+0x4000,ROMMap[V],0x4000);
else memset(RAM+0x4000,NORAM,0x4000);
if(Verbose&0x08) printf("ROM: Bank %d selected\n",V);
}
return;
case 0x4000:
V&=RAMMask;
if(!MBCType&&(RAMBank!=V))
{
if(RAMMap[RAMBank]) memcpy(RAMMap[RAMBank],RAM+0xA000,0x2000);
RAMBank=V;
if(RAMMap[V]) memcpy(RAM+0xA000,RAMMap[V],0x2000);
else memset(RAM+0xA000,NORAM,0x2000);
if(Verbose&0x08) printf("RAM: Bank %d selected\n",V);
}
return;
case 0x8000:
case 0xA000:
case 0xC000:
RAM[A]=V;return;
case 0xE000:
DoWrite(A,V);return;
}
}
void DoWrite(word A,byte V)
{
static byte TPShifts[] = { 6,0,2,4 };
switch(A)
{
case 0xFF00: JOYPAD=0xCF|V;
if(!(V&0x20)) JOYPAD&=(JoyState>>4)|0xF0;
if(!(V&0x10)) JOYPAD&=JoyState|0xF0;
return;
case 0xFF01: SerialBuf=V;return;
case 0xFF02: if(V&0x80)
{
IFLAGS|=0x08;
if(SerialOut) fputc(SerialBuf,SerialOut);
}
V|=0x7E;break;
case 0xFF05: V=0;break;
case 0xFF07: TPeriod=1<<TPShifts[V&0x03];
V|=0xF8;break;
case 0xFF0F: V|=0xE0;break;
case 0xFFFF: V|=0xE0;break;
case 0xFF46: memcpy(RAM+0xFE00,RAM+((word)V<<8),0xA0);
return;
case 0xFF41: V=(V&0xF8)|(LCDSTAT&0x07);
break;
case 0xFF40: ChrGen=RAM+(V&0x10? 0x8000:0x8800);
BgdTab=RAM+(V&0x08? 0x9C00:0x9800);
WndTab=RAM+(V&0x40? 0x9C00:0x9800);
break;
case 0xFF44: V=0;break;
case 0xFF47: BPal[0]=V&0x03;
BPal[1]=(V&0x0C)>>2;
BPal[2]=(V&0x30)>>4;
BPal[3]=(V&0xC0)>>6;
break;
case 0xFF48: SPal0[0]=V&0x03;
SPal0[1]=(V&0x0C)>>2;
SPal0[2]=(V&0x30)>>4;
SPal0[3]=(V&0xC0)>>6;
break;
case 0xFF49: SPal1[0]=V&0x03;
SPal1[1]=(V&0x0C)>>2;
SPal1[2]=(V&0x30)>>4;
SPal1[3]=(V&0xC0)>>6;
break;
}
RAM[A]=V;
}
int StartGB(char *CartName)
{
static char *CartTypes[] =
{
"ROM ONLY","ROM+MBC1","ROM+MBC1+RAM",
"ROM+MBC1+RAM+BATTERY","UNKNOWN",
"ROM+MBC2","ROM+MBC2+BATTERY"
};
static struct { word Code;char *Name; } Companies[] =
{
{ 0x3301,"Nintendo" },{ 0x7901,"Accolade" },{ 0xA400,"Konami" },
{ 0x6701,"Ocean" },{ 0x5601,"LJN" },{ 0x9900,"ARC?" },
{ 0x0101,"Nintendo" },{ 0x0801,"Capcom" },{ 0x0100,"Nintendo" },
{ 0xBB01,"SunSoft" },{ 0xA401,"Konami" },{ 0xAF01,"Namcot?" },
{ 0xCA01,"Palcom?" },{ 0xB601,"HAL?" },{ 0x9C01,"Imagineer" },
{ 0xB101,"NexSoft?" },{ 0x5101,"Acclaim?" },{ 0x6001,"Titus?" },
{ 0xB601,"HAL?" },{ 0xCA01,"Palcom?" },{ 0x3300,"Nintendo?" },
{ 0x5401,"Gametek?" },{ 0x7F01,"Kemco?" },{ 0x4901,"Irem?" },
{ 0x7001,"Infogrames?" },{ 0x8B01,"Bullet-Proof Software?" },
{ 0x6101,"Virgin Games" },{ 0x5501,"Park Place?" },
{ 0x5D01,"Tradewest?" },{ 0x6F01,"ElectroBrain?" },
{ 0xAA01,"Broderbund?" },{ 0xC301,"SquareSoft?" },
{ 0x5201,"Activision?" },{ 0x5A01,"Bitmap Brothers/Mindscape" },
{ 0x5301,"American Sammy" },{ 0x1801,"Hudson Soft"},{ 0x0000,NULL }
};
int Checksum,I,J,*T;
FILE *F;
char *P;
word A;
reg R;
/*** STARTUP CODE starts here: ***/
T=(int *)"\01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
#ifdef LSB_FIRST
if(*T!=1)
{
puts("********** This machine is high-endian. *********");
puts("Take #define LSB_FIRST out and compile VGB again.");
return(0);
}
#else
if(*T==1)
{
puts("********* This machine is low-endian. *********");
puts("Insert #define LSB_FIRST and compile VGB again.");
return(0);
}
#endif
for(I=0;I<256;I++) RAMMap[I]=ROMMap[I]=NULL;
SerialIn=stdin;SerialOut=NULL;
if(Verbose) printf("Allocating 64kB for address space...");
if(!(RAM=malloc(0x10000)))
{ if(Verbose) puts("FAILED");return(0); }
memset(RAM,0,0x10000);
if(Verbose) printf("OK\nOpening %s...",CartName);
if(!(F=fopen(CartName,"rb")))
{ if(Verbose) puts("FAILED");return(0); }
if(Verbose) printf("reading...");
if(fread(RAM,1,0x4000,F)!=0x4000)
{ if(Verbose) puts("FAILED");return(0); }
ROMMap[0]=RAM;
ROMBanks=2<<RAM[0x0148];
RAMBanks=(1<<RAM[0x0149]*2)>>1;
Checksum=((word)RAM[0x014E]<<8)+RAM[0x014F];
MBCType=RAM[0x0147]>3;
P=NULL;
if((RAM[0x0147]==4)||(RAM[0x0147]>6)) P="Unknown ROM type";
if(RAM[0x0100]||(RAM[0x0101]!=0xC3)) P="Invalid cartridge ROM";
if(P)
{
printf("\nError loading cartridge: %s\n",P);
fclose(F);return(0);
}
if(Verbose)
{
printf("OK\n Name: %s\n",RAM+0x0134);
printf(" Type: %s\n",CartTypes[RAM[0x0147]]);
printf(" ROM Size: %dx16kB\n",ROMBanks);
printf(" RAM Size: %dx8kB\n",RAMBanks);
J=((word)RAM[0x014B]<<8)+RAM[0x014A];
for(I=0,P=NULL;!P&&Companies[I].Name;I++)
if(J==Companies[I].Code) P=Companies[I].Name;
printf(" Manufacturer ID: %Xh",J);
printf(" [%s]\n",P? P:"?");
printf(" Version Number: %Xh\n",RAM[0x014C]);
printf(" Complement Check: %Xh\n",RAM[0x014D]);
printf(" Checksum: %Xh\n",Checksum);
J=((word)RAM[0x0103]<<8)+RAM[0x0102];
printf(" Start Address: %Xh\n",J);
}
Checksum+=RAM[0x014E]+RAM[0x014F];
for(I=0;I<0x4000;I++) Checksum-=RAM[I];
if(Verbose) printf("Loading %dx16kB ROM banks:\n.",ROMBanks);
for(I=1;I<ROMBanks;I++)
if(ROMMap[I]=malloc(0x4000))
if(fread(ROMMap[I],1,0x4000,F)==0x4000)
{
for(J=0;J<0x4000;J++) Checksum-=ROMMap[I][J];
if(Verbose) putchar('.');
}
else { if(Verbose) puts("READ FAILED");break; }
else { if(Verbose) puts("MALLOC FAILED");break; }
fclose(F);if(I<ROMBanks) return(0);
if(Checksum&0xFFFF)
{
printf("\nError loading cartridge: Checksum is wrong\n\
(Should be $0000 - is $%lx !)\nStart anyway (y/n)?",(Checksum&0xffff));
if(getchar() != 'y') return(0);
}
if(RAMBanks&&Verbose)
printf("OK\nAllocating %dx8kB RAM banks...",RAMBanks);
for(I=0;I<RAMBanks;I++)
if(RAMMap[I]=malloc(0x2000))
memset(RAMMap[I],0,0x2000);
else
{ if(Verbose) puts("FAILED");return(0); }
puts("OK");
if(((RAM[0x0147]==3)||(RAM[0x0147]==6))&&RAMMap[0])
if(SaveName=malloc(strlen(CartName)+10))
{
strcpy(SaveName,CartName);
if(P=strrchr(SaveName,'.')) strcpy(P,".sav");
else strcat(SaveName,".sav");
if(Verbose) printf("Opening %s...",SaveName);
if(F=fopen(SaveName,"rb"))
{
if(Verbose) printf("reading...");
J=(fread(RAMMap[0],1,0x2000,F)==0x2000);
if(Verbose) puts(J? "OK":"FAILED");
fclose(F);
}
else if(Verbose) puts("FAILED");
}
if(CheatCount>0)
{
if(Verbose) puts("Patching cheats into the ROM code:");
for(J=0;J<CheatCount;J++)
{
I=Cheats[J].Bank;A=Cheats[J].Address;
printf(" %d:%Xh = %Xh - ",I,A,Cheats[J].Value);
if(I<ROMBanks)
if(ROMMap[I])
{ *(ROMMap[I]+A)=Cheats[J].Value;P="OK"; }
else P="FAILED. No ROM in this bank.";
else P="FAILED. Invalid bank number.";
puts(P);
}
}
for(ROMMask=0x01,I=ROMBanks;I;ROMMask<<=1,I>>=1);ROMMask--;ROMBank=1;
for(RAMMask=0x01,I=RAMBanks;I;RAMMask<<=1,I>>=1);RAMMask--;RAMBank=0;
if(RAMMap[0]) memcpy(RAM+0xA000,RAMMap[0],0x2000);
if(ROMMap[1]) memcpy(RAM+0x4000,ROMMap[1],0x4000);
IPeriod=VPeriod/154;TPeriod=64;
ChrGen=RAM+0x8800;BgdTab=WndTab=RAM+0x9800;
LCDCONT=0x81;LCDSTAT=0x00;
CURLINE=0x00;CMPLINE=0xFF;
IFLAGS=ISWITCH=0xE0;
TIMECNT=TIMEMOD=0x00;
TIMEFRQ=0xF8;
SIOCONT=0x00;
for(I=0;I<4;I++) SPal0[I]=SPal1[I]=BPal[I]=I;
BGRDPAL=SPR0PAL=SPR1PAL=0xE4;
R.PC.W=0x0100;R.SP.W=0xF000;R.IFF=0;
if(Verbose) printf("RUNNING ROM CODE...\n");
A=Z80(RAM,R);
if(Verbose) printf("EXITED at PC = %Xh.\n",A);
return(1);
}
void TrashGB(void)
{
FILE *F;
int I;
if(SaveName&&RAMMap[0])
{
if(Verbose) printf("\nOpening %s...",SaveName);
if(F=fopen(SaveName,"wb"))
{
if(Verbose) printf("writing...");
I=(fwrite(RAMBank? RAMMap[0]:(RAM+0xA000),1,0x2000,F)==0x2000);
if(Verbose) puts(I? "OK":"FAILED");
fclose(F);
}
else if(Verbose) puts("FAILED");
}
if(SaveName) free(SaveName);
if(RAM) free(RAM);
for(I=1;ROMMap[I];I++) free(ROMMap[I]);
for(I=0;RAMMap[I];I++) free(RAMMap[I]);
}
word Interrupt(void)
{
static int TCount=1;
static int UCount=1;
register word A;
register byte J;
A=0xFFFF;DIVREG++;
SprFlag|=LCDCONT&0x02;
J=(0x08<<(LCDSTAT&0x03))&0x3F;
if(LineDelay)
{
if(!UCount&&(CURLINE<144)) RefreshLine(CURLINE);
CURLINE=(CURLINE>=153)? 0:CURLINE+1;
if(CURLINE==CMPLINE) { LCDSTAT|=0x04;J|=0x40; }
}
else
{
if(CURLINE==CMPLINE) { LCDSTAT|=0x04;J|=0x40; }
CURLINE=(CURLINE>=153)? 0:CURLINE+1;
if(!UCount&&(CURLINE<144)) RefreshLine(CURLINE);
}
/* If end of frame reached... */
if(CURLINE==145)
{
if(!UCount)
{
if((LCDCONT&0x80)&&SprFlag) RefreshSprites();
SprFlag=0;UCount=UPeriod;RefreshScreen();
}
else UCount--;
/* Generating VBlank interrupt */
if((ISWITCH&VBL_IFLAG)&&(LCDCONT&0x80))
{ IFLAGS|=VBL_IFLAG;A=0x0040; }
}
/* Generating LCD controller interrupt */
if((J&LCDSTAT)&&(ISWITCH&LCD_IFLAG)&&(LCDCONT&0x80))
{ IFLAGS|=LCD_IFLAG;A=0x0048; }
/* Generating timer interrupt */
if(TIMEFRQ&0x04)
if(!--TCount)
{
TCount=TPeriod;
if(!++TIMECNT)
{
if(ISWITCH&TIM_IFLAG)
{ IFLAGS|=TIM_IFLAG;A=0x0050; }
TIMECNT=TIMEMOD;
}
}
/* Generating serial IO interrupt */
if(0/*SIOCONT&0x80*/)
{
SIOCONT&=0x7F;
if(ISWITCH&SIO_IFLAG)
{ IFLAGS|=SIO_IFLAG;A=0x0058; }
}
if(CURLINE==144)
{
IFLAGS=0x00;
/* Updating joystick */
J=0xCF|JOYPAD;JoyState=Joystick();
if(!(J&0x10)) J&=JoyState|0xF0;
if(!(J&0x20)) J&=(JoyState>>4)|0xF0;
/* Generating joystick interrupt */
if((J^JOYPAD)&JOYPAD)
if(ISWITCH&JOY_IFLAG)
{ IFLAGS|=JOY_IFLAG;A=0x0060; }
JOYPAD=J;
}
return(A);
}
int AddCheat(char *Cheat)
{
static char Digits[]="0123456789ABCDEF";
int X1,X2;
if(Verbose){ puts("Cheats are not supported yet!"); return(1);}
if(CheatCount>=MAXCHEAT) return(0);
else
{
if((Cheat[3]!='-')||(Cheat[7]!='-')) return(0);
X1=strchr(Digits,toupper(Cheat[0]))-Digits;
X2=strchr(Digits,toupper(Cheat[1]))-Digits;
if((X1<0)||(X2<0)) return(0);
else Cheats[CheatCount].Value=X1*16+X2;
X1=strchr(Digits,toupper(Cheat[5]))-Digits;
X2=strchr(Digits,toupper(Cheat[4]))-Digits;
if((X1<0)||(X2<0)) return(0);
else Cheats[CheatCount].Address=X1*16+X2;
X1=strchr(Digits,toupper(Cheat[2]))-Digits;
X2=strchr(Digits,toupper(Cheat[6]))-Digits;
if((X1<0)||(X2<0)) return(0);
else Cheats[CheatCount].Address+=256*(X1*16+X2);
X1=strchr(Digits,toupper(Cheat[2]))-Digits;
if(X1<0) return(0); else Cheats[CheatCount].Bank=X1;
if(Cheats[CheatCount].Address<0x4000)
Cheats[CheatCount].Bank=0;
else
if(Cheats[CheatCount].Address<0x8000)
Cheats[CheatCount].Address-=0x4000;
else
return(0);
CheatCount++;return(1);
}
}